#include <stdio.h>
#include "opt.h"
#include "eth.h"
#include "flow.h"
#include "term.h"

#define MAX_FLOW 100

static char           *ifname = NULL;
static flow            flows[MAX_FLOW];
static pthread_mutex_t flow_mutex;
static int             nflows = 0;
static int             packet_max = 0;
static int             npacket = 0;
static pthread_t       poll_rx_thread;
static int             quit = 0;
static int             curses = 1;
static int             line = 0;





static void 
print_help() 
{



}

static int 
parse_options(int    argc, 
              char  *argv[], 
              char **ifname,   
              flow  *flows,
              int   *nflows,
              int   *packet_max) 
{
    char *sopt;
    int   soptindex = 1;
    IP    dst_addr; 
    flow *flow;
 
    *ifname = NULL;
    *nflows = 0;

    while (sopt = sgetopt(argc, argv, &soptindex)) {
        if (strcmp(sopt, "ip") == 0) {
            if (soptarg != NULL) {
                dst_addr = inet_addr(soptarg);  
                if (dst_addr == INADDR_NONE) {
                    fprintf(stderr, "ERROR: bad address: %s\n", soptarg);
                    return -1;
                } 
                if (*nflows > MAX_FLOW) {
                    fprintf(stderr, "ERROR: max addresses ( > %d)\n", MAX_FLOW);
                    return -1;
                }
                flow = &flows[(*nflows)++];
                flow->dip = dst_addr;
            } else {
                fprintf(stderr, "ERROR: missing address after -ip option\n");
                return -1;
            }
        } else if (strcmp(sopt, "if") == 0) {
            if (soptarg != NULL) {
                *ifname = soptarg;
            } else {
                fprintf(stderr, "ERROR: missing interface after -if option\n");
                return -1;
            }
        } else if (strcmp(sopt, "count") == 0) {
            if (soptarg != NULL) {
                    if (soptarg[0] == '0' && strlen(soptarg) == 1) {
                    fprintf(stderr, "INFO: infinite packets implied " \
                                    "(count == 0)\n");
                    *packet_max = 0;
                } else {
                    *packet_max = atoi(soptarg);
                     if (*packet_max <= 0) {
                         fprintf(stderr, "ERROR: invalid count: %s\n", soptarg);
                         return -1;
                     }
                }
            } else {
                fprintf(stderr, "ERROR: missing interations after -count option\n");
                return -1;
            }
        } else if (strcmp(sopt, "f") == 0) {
            if (soptarg != NULL) { 
                *nflows = parse_flows(soptarg, flows, MAX_FLOW);

                if (*nflows < 0) {
                    return -1;
                } else if (*nflows == 0) {
                    
                }
            } else {
                fprintf(stderr, "ERROR: missing file after -f option\n");
                return -1;
            }
        } else {
            fprintf(stderr, "WARNING: unrecognized option: %s\n", sopt);
        }
    }

    if (*ifname == NULL) {
        fprintf(stderr, "ERROR: no interface name provided\n");
        return -1;
    }

    if (*nflows == 0) {
        fprintf(stderr, "ERROR: no traffic destinations provided\n");
        return -1;
    }

  
    return 0;
}



static int 
search_flows(IP dstip) 
{
    int   index;
    flow *flow;

    for (index = 0; index < nflows; index++) {
        flow = &flows[index];
        if (flow->dip == dstip) {
            return index;
        }
    }
     
    return -1;
}


static void
print_rx_flows_header(void) 
{
    int   index;
    flow *flow;
    IN    dst;

    printf("\nRx traffic from these %d destinations via %s:\n\n", nflows, ifname);

    for (index = 0; index < nflows; index++) {
        flow = &flows[index];
        dst.s_addr = flow->dip;
        printf("%s\n", inet_ntoa(dst));
    }

    printf("\nRx rates:\n\n");
    fflush(stdout);
}


static void
refresh_rx_flow_rates(void)
{
    int   index;
    flow *flow;
    IN    dst;

    line = 0;

    if (curses) {
        tprint(line++, 0, "Rx Traffic from these %d destinations via [%s]", nflows, ifname);
        line++;
        tprint(line,   0, "IP Destination");
        tprint(line,  18, "Rx Rate");
        tprint(line,  30, "Total Rx");
        line++;
        line++;
    }

    for (index = 0; index < nflows; index++, line++) {
        flow = &flows[index];
        dst.s_addr = flow->dip;

        if (curses) {
            attron(A_BOLD);
            tprint(line,  0, "%s ",    inet_ntoa(dst));
            attroff(A_BOLD);
            tprint(line, 18, "%d P/s    ", flow->recv);
            tprint(line, 30, "%d Packets", flow->c_recv);
        } else {
            printf("[%s] %2d P/s   ", inet_ntoa(dst), flow->recv);
        }
        
        flow->recv = 0;
    }

    if (curses) {
        line++;
        tprint(line,  0, "TOTAL  ");
        tprint(line, 30, "%d Packets    ", npacket);
        line++;
        tprint(line, 0, "");
    } else {
        printf("[TOTAL] %d Packets %s \r", npacket, "      ");
    }
    
    if (curses) {
        refresh(); 
    } else {
        fflush(stdout); 
    }

}




static void 
print_rx_flow_totals(void) 
{
    
}


static void *
poll_rx_flows(void *arg) 
{
    int    index;
    flow  *flow;
    IN     dst;
 
    while (!quit) {
  
        pthread_mutex_lock(&flow_mutex);

        refresh_rx_flow_rates();

        pthread_mutex_unlock(&flow_mutex);

        sleep(1);
    }
}    


/*
 * Since we are blocking on receiving packets over the wire, we have to exit
 * immediately in the cleanup code called when Ctrl+C is pressed... 
 */
static void 
cleanup()
{
    if (curses) { 
        endwin();
    } else {
        printf("\n\n");
    }
  
    exit(1);
}


int 
main(int argc, char *argv[])
{
    IF     *intf;
    flow   *flow;
    int     findex;
    IP      dst_addr;
    IN      dst_inaddr;
    int     index = 0;
    int     iter = 0;
    char   *packet[ETH_BUF_LEN];
    int     plen = 0;
    int     rc = 0;
    int     soptindex = 1;
    char   *sopt;
    struct  ip iphdr;

    signal(SIGINT, cleanup);

    memset(flows, 0, sizeof(flow) * MAX_FLOW);

    rc = parse_options(argc, argv, &ifname, flows, &nflows, &packet_max);

    if (rc < 0) {
        return rc;
    }

    /*
     * Initialize the interface
     */
    intf = eth_interface_init(ifname, 1);

    if (intf == NULL) {
        return -1;
    }

    if (!curses) {
        print_rx_flows_header();
    }

    if (curses) {
        initscr();
    }

    /*
     * Create the poll thread that will poll the Rx rate every second
     */
    rc = pthread_create(&poll_rx_thread, NULL, poll_rx_flows, NULL);
    
    if (rc != 0) {
        fprintf(stderr, "ERROR: Rx poll thread creation failed: %d\n", rc);
        return -1;
    } 

    /*
     * Main packet Rx loop. For now, we loop forever.
     */
    while (1) {
        rc = eth_packet_recv(packet, ETH_BUF_LEN, intf);
        if (rc < 0) {
            fprintf(stderr, "ERROR: packet rx failed: %d\n", rc);
            continue;
        } else {
            plen = rc;
        }

        rc = eth_packet_parse_ip(packet, plen, &iphdr);
        if (rc != 0) {
            fprintf(stderr, "ERROR: packet parse failed: %d\n", rc);
            continue;
        } 

        dst_addr = iphdr.ip_dst.s_addr;
        dst_inaddr.s_addr = dst_addr;

        pthread_mutex_lock(&flow_mutex);
    
        findex = search_flows(dst_addr);

            if (findex >= 0) {
                flow = &flows[findex];   
                flow->recv++; 
                flow->c_recv++;
                npacket++;
            }
       
        pthread_mutex_unlock(&flow_mutex);
       
    }

}
